//在本问题中，有根树指满足以下条件的 有向 图。该树只有一个根节点，所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点，而根节
//点没有父节点。 
//
// 输入一个有向图，该图由一个有着 n 个节点（节点值不重复，从 1 到 n）的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间，这条
//附加的边不属于树中已存在的边。 
//
// 结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi]，用以表示 有向 图中连接顶点 ui 和顶点 vi 的边，其中 ui 是 
//vi 的一个父节点。 
//
// 返回一条能删除的边，使得剩下的图是有 n 个节点的有根树。若有多个答案，返回最后出现在给定二维数组的答案。 
//
// 
//
// 示例 1： 
//
// 
//输入：edges = [[1,2],[1,3],[2,3]]
//输出：[2,3]
// 
//
// 示例 2： 
//
// 
//输入：edges = [[1,2],[2,3],[3,4],[4,1],[1,5]]
//输出：[4,1]
// 
//
// 
//
// 提示： 
//
// 
// n == edges.length 
// 3 <= n <= 1000 
// edges[i].length == 2 
// 1 <= ui, vi <= n 
// 
// Related Topics深度优先搜索 | 广度优先搜索 | 并查集 | 图 
//
// 👍 336, 👎 0 
//
//
//
//

package leetcode.editor.cn;

class RedundantConnectionIi {
    public static void main(String[] args) {
        Solution solution = new RedundantConnectionIi().new Solution();
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // https://leetcode.cn/problems/redundant-connection-ii/solution/bing-cha-ji-java-by-liweiwei1419/
        public int[] findRedundantDirectedConnection(int[][] edges) {
            // 边的条数
            int n = edges.length;

            // 求入度
            int[] inDegree = new int[n + 1];
            for (int[] edge : edges) {
                inDegree[edge[1]]++;
            }

            // 从后往前(因为返回的是后边的边),删除入度为2的节点，判断是否形成环,如果不构成环，那么就是需要删除的边
            for (int i = n - 1; i >= 0; i--) {
                int[] edge = edges[i];
                if (inDegree[edge[1]] == 2 && !judgeCircle(edges, n, i)) {
                    return edge;
                }
            }

            // 从后往前(因为返回的是后边的边),删除入度为1的节点，判断是否形成环,如果不构成环，那么就是需要删除的边
            for (int i = n - 1; i >= 0; i--) {
                int[] edge = edges[i];
                if (inDegree[edge[1]] == 1 && !judgeCircle(edges, n, i)) {
                    return edge;
                }
            }

            return new int[]{};
        }

        /**
         * 将 removeEdgeIndex 去掉以后，剩下的有向边是否构成环
         *
         * @param edges
         * @param len             结点总数（从 1 开始，因此初始化的时候 + 1）
         * @param removeEdgeIndex 删除的边的下标
         * @return 构成环，返回 true
         */
        private boolean judgeCircle(int[][] edges, int len, int removeEdgeIndex) {
            UF uf = new UF(len + 1);
            for (int i = 0; i < edges.length; i++) {
                // 去掉这条边
                if (i == removeEdgeIndex) continue;
                // 在同一个连通分量，说明在同一个环
                if (!uf.union(edges[i][0], edges[i][1])) {
                    return true;
                }
            }
            return false;
        }

        class UF {
            int count;
            int[] parent;

            public UF(int n) {
                this.count = n;
                this.parent = new int[n];
                for (int i = 0; i < n; i++) {
                    parent[i] = i;
                }
            }

            public int find(int x) {
                if (x != parent[x]) {
                    parent[x] = find(parent[x]);
                }
                return parent[x];
            }

            public boolean union(int p, int q) {
                int rootQ = find(q);
                int rootP = find(p);
                if (rootP == rootQ) return false;
                parent[rootP] = rootQ;
                return true;
            }
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
